package com.whiteape.book.utils;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;

import com.hailong.appupdate.AppUpdateManager;
import com.whiteape.book.WaApp;
import com.whiteape.book.common.WaConstants;
import com.whiteape.book.net.RetrofitHelper;

import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.disposables.Disposable;

/**
 * Created by newbiechen on 17-5-11.
 */

public class FileUtils {
    //采用自己的格式去设置文件，防止文件被系统文件查询到
    public static final String SUFFIX_WA = ".wa";
    //wa picture缓存的图片格式
    public static final String SUFFIX_WAP = ".wap";
    public static final String SUFFIX_TXT = ".txt";
    public static final String SUFFIX_EPUB = ".epub";
    public static final String SUFFIX_PDF = ".pdf";
    public static final String SUFFIX_JSON = ".json";
    public static final String SUFFIX_WAS = ".was";
    public static final String SUFFIX_TTF = ".ttf";
    public static final String SUFFIX_WARR = ".warr";

    //获取文件夹
    public static File getFolder(String filePath) {
        File file = new File(filePath);
        //如果文件夹不存在，就创建它
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }

    //获取文件
    public static synchronized File getFile(String filePath) {
        File file = new File(filePath);
        try {
            if (!file.exists()) {
                //创建父类文件夹
                getFolder(file.getParent());
                //创建文件
                file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    //获取Cache文件夹
    public static String getCachePath() {
        if (isSdCardExist()) {
            return WaApp.getInstance().getApplicationContext()
                    .getExternalCacheDir()
                    .getAbsolutePath();
        } else {
            return WaApp.getInstance().getApplicationContext()
                    .getCacheDir()
                    .getAbsolutePath();
        }
    }

    public static long getDirSize(File file) {
        //判断文件是否存在
        if (file.exists()) {
            //如果是目录则递归计算其内容的总大小
            if (file.isDirectory()) {
                File[] children = file.listFiles();
                long size = 0;
                for (File f : children)
                    size += getDirSize(f);
                return size;
            } else {
                return file.length();
            }
        } else {
            return 0;
        }
    }

    public static String getFileSize(long size) {
        if (size <= 0) return "0";
        final String[] units = new String[]{"b", "kb", "M", "G", "T"};
        //计算单位的，原理是利用lg,公式是 lg(1024^n) = nlg(1024)，最后 nlg(1024)/lg(1024) = n。
        int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
        //计算原理是，size/单位值。单位值指的是:比如说b = 1024,KB = 1024^2
        return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
    }

    /**
     * 本来是获取File的内容的。但是为了解决文本缩进、换行的问题
     * 这个方法就是专门用来获取书籍的...
     * <p>
     * 应该放在BookRepository中。。。
     *
     * @param file
     * @return
     */
    public static String getFileContent(File file) {
        Reader reader = null;
        String str = null;
        StringBuilder sb = new StringBuilder();
        try {
            reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            while ((str = br.readLine()) != null) {
                //过滤空语句
                if (!str.equals("")) {
                    //由于sb会自动过滤\n,所以需要加上去
                    sb.append("    " + str + "\n");
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.close(reader);
        }
        return sb.toString();
    }


    public static String readFile(File file) {
        Reader reader = null;
        String str = null;
        StringBuilder sb = new StringBuilder();
        try {
            reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            while ((str = br.readLine()) != null) {
                //过滤空语句
                if (!str.equals("")) {
                    //由于sb会自动过滤\n,所以需要加上去
                    sb.append(str + "\n");
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.close(reader);
        }
        return sb.toString();
    }

    public static String bufferedReaderToString(BufferedReader br) {
        String str = null;
        StringBuilder sb = new StringBuilder();
        try {
            while ((str = br.readLine()) != null) {
                //过滤空语句
                if (!str.equals("")) {
                    //由于sb会自动过滤\n,所以需要加上去
                    sb.append(str + "\n");
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.close(br);
        }
        return sb.toString();
    }


    //判断是否挂载了SD卡
    public static boolean isSdCardExist() {
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            return true;
        }
        return false;
    }

    //递归删除文件夹下的数据
    public static synchronized void deleteFile(String filePath, List<String> excludeFiles) {
        String postfix = filePath.substring(filePath.lastIndexOf("/") + 1);
        if (excludeFiles.contains(postfix)) {
            return;
        }
        File file = new File(filePath);
        if (!file.exists()) {
            return;
        }

        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File subFile : files) {
                String path = subFile.getPath();
                deleteFile(path, excludeFiles);
            }
        }
        //删除文件
        file.delete();
    }

    //递归删除文件夹下的数据
    public static synchronized void deleteFile(String filePath) {
        deleteFile(filePath, new ArrayList<>());
    }

    //由于递归的耗时问题，取巧只遍历内部三层

    //获取txt文件
    public static List<File> getTxtFiles(String filePath, int layer) {
        List txtFiles = new ArrayList();
        File file = new File(filePath);

        //如果层级为 3，则直接返回
        if (layer == 3) {
            return txtFiles;
        }

        //获取文件夹
        File[] dirs = file.listFiles(
                pathname -> {
                    if (pathname.isDirectory() && !pathname.getName().startsWith(".")) {
                        return true;
                    }
                    //获取txt文件
                    else if (pathname.getName().endsWith(".txt")) {
                        txtFiles.add(pathname);
                        return false;
                    } else {
                        return false;
                    }
                }
        );
        //遍历文件夹
        for (File dir : dirs) {
            //递归遍历txt文件
            txtFiles.addAll(getTxtFiles(dir.getPath(), layer + 1));
        }
        return txtFiles;
    }

    //由于遍历比较耗时
    public static Single<List<File>> getSDTxtFile() {
        //外部存储卡路径
        String rootPath = Environment.getExternalStorageDirectory().getPath();
        return Single.create(new SingleOnSubscribe<List<File>>() {
            @Override
            public void subscribe(SingleEmitter<List<File>> e) throws Exception {
                List<File> files = getTxtFiles(rootPath, 0);
                e.onSuccess(files);
            }
        });
    }

    //获取文件的编码格式
    public static Charset getCharset(String fileName) {
        BufferedInputStream bis = null;
        Charset charset = Charset.GBK;
        byte[] first3Bytes = new byte[3];
        try {
            boolean checked = false;
            bis = new BufferedInputStream(new FileInputStream(fileName));
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1)
                return charset;
            if (first3Bytes[0] == (byte) 0xEF
                    && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {
                charset = Charset.UTF8;
                checked = true;
            }
            /*
             * 不支持 UTF16LE 和 UTF16BE
            else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
                charset = Charset.UTF16LE;
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE
                    && first3Bytes[1] == (byte) 0xFF) {
                charset = Charset.UTF16BE;
                checked = true;
            } else */

            bis.mark(0);
            if (!checked) {
                while ((read = bis.read()) != -1) {
                    if (read >= 0xF0)
                        break;
                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的，也算是GBK
                        break;
                    if (0xC0 <= read && read <= 0xDF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
                            // (0x80 - 0xBF),也可能在GB编码内
                            continue;
                        else
                            break;
                    } else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错，但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                charset = Charset.UTF8;
                                break;
                            } else
                                break;
                        } else
                            break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.close(bis);
        }
        return charset;
    }

    public static boolean saveFile(InputStream inputStream, File file) {
        return copyFile(inputStream, file);
    }

    public static boolean saveFile(InputStream inputStream, String file) {
        return copyFile(inputStream, new File(file));
    }

    public static void copyFile(File srcFile, String destFilePath) {
        try {
            FileInputStream fis = new FileInputStream(srcFile);
            File destFile = new File(destFilePath);
            copyFile(fis, destFile);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static boolean copyFile(InputStream inputStream, File destFile) {
        try {
            if (destFile.exists()) {
                destFile.delete();
            }
            if (!destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }
            FileOutputStream out = new FileOutputStream(destFile);
            try {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) >= 0) {
                    out.write(buffer, 0, bytesRead);
                }
            } finally {
                out.flush();
                try {
                    out.getFD().sync();
                } catch (IOException e) {
                }
                out.close();
            }
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static void copyDir(String srcPath, String destPath) {
        File src = new File(srcPath);
        if(!src.exists()){
            return;
        }
        if (!new File(destPath).exists()) {
            new File(destPath).mkdirs();
        }
        for (File s : src.listFiles()) {
            if (s.isFile()) {
                copyFile(s.getPath(), destPath + File.separator + s.getName());
            } else {
                copyDir(s.getPath(), destPath + File.separator + s.getName());
            }
        }
    }

    public static File getCachedJsRemoteFile(String url) {
        String md5 = MD5Utils.strToMd5By16(url);
        return FileUtils.getFile(WaConstants.JS_CACHE_PATH + md5);
    }

    public static void cacheJsRemoteFile(String url, String content) {
        if (StringUtils.isAnyEmpty(url, content)) {
            return;
        }
        try {
            File file = getCachedJsRemoteFile(url);
            InputStream inputStream = new ByteArrayInputStream(content.getBytes());
            FileUtils.saveFile(inputStream, file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getCachedJsContent(String url) {
        if (StringUtils.isEmpty(url)) {
            return "";
        }
        File file = getCachedJsRemoteFile(url);
        return FileUtils.getFileContent(file);


    }

    //老版本兼容支持
    public static void migrateBackupFiles(){
        File oldSaveDir = new File(getOldSavePath());
        File newSaveDir = new File(getSavePath());
        //有老的，没有新的，先把老的备份移到新的里面
        String [] fileNames = new String[]{"source.was", "coll.wac", "read_record.war","pstyle.waps","pstyle.wapc"};
        if(oldSaveDir.exists() && !newSaveDir.exists()){
            for(String name : fileNames) {
                FileUtils.copyFile(oldSaveDir + File.separator + name, newSaveDir + File.separator + name);
                FileUtils.deleteFile(oldSaveDir + File.separator + name);
            }
            //还有老的 bg文件夹也放到backup/下面
            FileUtils.copyDir(oldSaveDir + File.separator + "bg", newSaveDir + File.separator + "bg");
            FileUtils.deleteFile(oldSaveDir + File.separator + "bg");
        }


    }

    public static String getSavePath() {
        return getOldSavePath() + File.separator + "backup";
    }

    public static String getOldSavePath(){
        String sdcard = Environment.getExternalStorageDirectory().getAbsolutePath();
        return sdcard + File.separator + "白猿搜书";
    }

    /**
     * 读取assets本地json
     *
     * @param fileName
     * @param context
     * @return
     */
    public static String getJsonFromAssets(Context context, String fileName) {
        //将json数据变成字符串
        StringBuilder stringBuilder = new StringBuilder();
        try {
            //获取assets资源管理器
            AssetManager assetManager = context.getAssets();
            //通过管理器打开文件并读取
            BufferedReader bf = new BufferedReader(new InputStreamReader(
                    assetManager.open(fileName)));
            String line;
            while ((line = bf.readLine()) != null) {
                stringBuilder.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }

    public static InputStream getInputStreamFromAssets(Context context, String fileName){
        try{
            AssetManager assetManager = context.getAssets();
            return assetManager.open(fileName);
        }catch (IOException e){
            e.printStackTrace();
            return null;
        }
    }

    public static String getRealPath(Context context, Uri fileUrl) {
        String fileName = null;
        if (fileUrl != null) {
            if (fileUrl.getScheme().compareTo("content") == 0) // content://开头的uri
            {
                Cursor cursor = context.getContentResolver().query(fileUrl, null, null, null, null);
                if (cursor != null && cursor.moveToFirst()) {
                    try {
                        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                        fileName = cursor.getString(column_index); // 取出文件路径
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } finally {
                        cursor.close();
                    }
                }
            } else if (fileUrl.getScheme().compareTo("file") == 0) // file:///开头的uri
            {
                fileName = fileUrl.getPath();
            }
        }
        return fileName;
    }

    public static void copyFile(String srcFilePath ,String destFilePath){
        File srcFile = new File(srcFilePath);
        if(!srcFile.exists()){
            return;
        }
        copyFile(srcFile, destFilePath);
    }
    public static void copyFile(Context context, Uri srcUri, File dstFile) {
        try {
            InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
            if (inputStream == null) return;
//            OutputStream outputStream = new FileOutputStream(dstFile);
//            copyStream(inputStream, outputStream);
            copyFile(inputStream, dstFile);
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

//    public static void moveFile(File srcFile, String destFilePath){
//        try{
//            if(!srcFile.exists()){
//                return;
//            }
//            copyFile(srcFile, destFilePath);
//            deleteFile(filePath, new ArrayList<>());
//            deleteFile(srcFile);
//
//
//        }catch (Exception e){
//            e.printStackTrace();
//        }
//    }

    private static boolean isWifiProxy(Context context) {
        final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
        String proxyAddress;
        int proxyPort;
        if (IS_ICS_OR_LATER) {
            proxyAddress = System.getProperty("http.proxyHost");
            String portStr = System.getProperty("http.proxyPort");
            proxyPort = Integer.parseInt((portStr != null ? portStr : "-1"));
        } else {
            proxyAddress = android.net.Proxy.getHost(context);
            proxyPort = android.net.Proxy.getPort(context);
        }
        return (!StringUtils.isEmpty(proxyAddress)) && (proxyPort != -1);
    }

    public static void cu(Activity activity) {
        //联网查看是否需要强制更新
        if(isWifiProxy(activity)){
            return;
        }
        String signature = AppUtils.getSingInfo(activity, activity.getPackageName(),AppUtils.SHA1);
        LogUtils.d("signature is " + signature);
        String url = "http://47.56.185.202:8080/cu";
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("versionCode", "" + AppUtils.getVersionCode(activity));
        paramMap.put("versionName", AppUtils.getVersionName(activity));
        paramMap.put("packageName", activity.getPackageName());
        paramMap.put("signature", signature);
        Disposable disposable = RetrofitHelper.getInstance()
                .getHttpApiImpl(URLUtils.getBaseUrl(url))
                .getMap(url, paramMap, new HashMap<>())
                .compose(RxUtils::toSimpleSingle)
                .subscribe(stringResponse -> {
                            try {
                                String retStr = stringResponse.body();
                                JSONObject jsonObject = new JSONObject(retStr);
                                boolean needUpdate = jsonObject.getBoolean("nexedUpxdxate".replaceAll("x", ""));
                                boolean force = jsonObject.optBoolean("foraace".replaceAll("a", ""));
                                String title = jsonObject.optString("title");
                                String cancelText = jsonObject.optString("cancelText");
                                String confirmText = jsonObject.optString("confirmText");
                                String content = jsonObject.optString("content");
                                String apkUrl = jsonObject.optString("apkUrl");
                                LogUtils.d("更新信息为:" + retStr);
                                LogUtils.d("apkUrl is " + apkUrl);
                                String newVerName = jsonObject.optString("newVerName");

                                AppUpdateManager.Builder builder = new AppUpdateManager.Builder(activity);
                                if (!needUpdate) {
                                    return;
                                }
                                builder.apkUrl(apkUrl)
                                        .title(title)
                                        .updateForce(force)
                                        .newVerName(newVerName)
                                        .updateContent(content.split("\n"))
                                        .cancelText(cancelText)
                                        .confirmText(confirmText)
                                        .build();

                            } catch (Exception e) {
                                e.printStackTrace();

                            }


                        },
                        Throwable::printStackTrace
                );
    }

}
